/*
 * SourceDirectoryOptionsPanel.java
 *
 * Created on December 5, 2007, 10:19 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package filecopier.gui.tabs;

import java.io.FilenameFilter;
import java.awt.Dimension;
import java.util.regex.Pattern;
import javax.swing.JFormattedTextField;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import java.text.NumberFormat;
import javax.swing.text.NumberFormatter;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import java.beans.PropertyChangeEvent;

import filecopier.Singleton;
import filecopier.ActionCommand;
import filecopier.options.Options;
import filecopier.options.Options.SrchFilter;
import filecopier.gui.Utilities;
import custom.io.RegExFilenameFilter;

/**
 *
 * @author david
 */
public class SourceDirectoryOptionsPanel extends JPanel implements ActionCommand {
    
    private JFormattedTextField[] maxDirLevelFld = new JFormattedTextField[Options.MAX_DIR_MERGE_IDX + 1];
    private Box[] maxDirLevelBox = new Box[Options.MAX_DIR_MERGE_IDX + 1];
    private Options options = null;
    private SrchFilterBox[] srchFilterBoxes = new SrchFilterBox[Options.DIR_MERGE_FILTER + 1];
    private int newMaxDirLevel = 0;

    /** Creates a new instance of SourceDirectoryOptionsPanel */
    public SourceDirectoryOptionsPanel() {
        super();
        initialize();
    }

    private void initialize() {
        this.options = Singleton.getInstance().getOptions();
        setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.PAGE_AXIS));

        add(getMaxDirLevelBox(Options.MAX_DIR_COPY_IDX, new JLabel("Copy "),
                "<html>Maximum level of directories to copy. The default entry of 0 means all<p>" +
                "levels of directories are copied.</html>"));

        add(getMaxDirLevelBox(Options.MAX_DIR_FLATTEN_IDX, new JLabel("Flatten "),
                "<html>Maximum level of directories to flatten. The default entry of -1 means no<p>" +
                "levels of directories are flattened. An entry of 0 means all levels of directories are flattened.</html>"));

        add(getMaxDirLevelBox(Options.MAX_DIR_MERGE_IDX, new JLabel("Merge "),
                "<html>Maximum level of directories to merge. The default entry of -1 means no<p>" +
                "levels of directories are merged. An entry of 0 means all levels of directories are merged.</html>"));

        add(new javax.swing.JSeparator());

        srchFilterBoxes[Options.FILE_COPY_FILTER] = new SrchFilterBox(SrchFilter.FileCopyFilter);
        add(srchFilterBoxes[Options.FILE_COPY_FILTER]);

        add(new javax.swing.JSeparator());

        srchFilterBoxes[Options.DIR_COPY_FILTER] = new SrchFilterBox(SrchFilter.DirCopyFilter);
        add(srchFilterBoxes[Options.DIR_COPY_FILTER]);

        add(new javax.swing.JSeparator());

        srchFilterBoxes[Options.DIR_FLATTEN_FILTER] = new SrchFilterBox(SrchFilter.DirFlattenFilter);
        add(srchFilterBoxes[Options.DIR_FLATTEN_FILTER]);

        add(new javax.swing.JSeparator());

        srchFilterBoxes[Options.DIR_MERGE_FILTER] = new SrchFilterBox(SrchFilter.DirMergeFilter);
        add(srchFilterBoxes[Options.DIR_MERGE_FILTER]);
    }

    private Box getMaxDirLevelBox(int idx, final JLabel prefix, final String dirLevelFldToolTipTxt) {
        if (idx < Options.MAX_DIR_COPY_IDX || idx > Options.MAX_DIR_MERGE_IDX) {
            throw new IllegalArgumentException("getMaxDirLevelBox was passed an index that is out of range");
        }
        if (maxDirLevelBox[idx] == null) {
            maxDirLevelBox[idx] = new Box(BoxLayout.X_AXIS);
            maxDirLevelBox[idx].add(prefix);
            maxDirLevelBox[idx].add(getMaxDirLevelFld(idx, dirLevelFldToolTipTxt));
            maxDirLevelBox[idx].add(new JLabel(" levels of directories."));
            maxDirLevelBox[idx].add(Box.createHorizontalGlue());
        }
        return maxDirLevelBox[idx];
    }

    private JFormattedTextField getMaxDirLevelFld(final int idx, final String toolTipTxt) {
        if (idx < Options.MAX_DIR_COPY_IDX || idx > Options.MAX_DIR_MERGE_IDX) {
            throw new IllegalArgumentException("getMaxDirLevelFld was passed an index that is out of range");
        }
        if (maxDirLevelFld[idx] == null) {
            // Set up number format.
            NumberFormat displayFormat = NumberFormat.getIntegerInstance();

            // Set up number formatter with number format.
            NumberFormatter displayFormatter = new NumberFormatter(displayFormat);
            displayFormatter.setValueClass(Integer.class);
            displayFormatter.setMinimum(idx == Options.MAX_DIR_COPY_IDX ? 0 : -1);

            maxDirLevelFld[idx] = new JFormattedTextField(displayFormatter);
            newMaxDirLevel = options.getMaxDirLevel(idx);
            maxDirLevelFld[idx].setValue(newMaxDirLevel);
            maxDirLevelFld[idx].setColumns(10);
            maxDirLevelFld[idx].setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT);
            maxDirLevelFld[idx].setToolTipText(toolTipTxt);
            maxDirLevelFld[idx].setComponentPopupMenu(Utilities.createEditPopupMenu(maxDirLevelFld[idx]));
            maxDirLevelFld[idx].setMaximumSize(new Dimension(42, maxDirLevelFld[idx].getPreferredSize().height));
            maxDirLevelFld[idx].addPropertyChangeListener(new java.beans.PropertyChangeListener() {

                public void propertyChange(PropertyChangeEvent evt) {
                    newMaxDirLevel = new Integer((Integer) maxDirLevelFld[idx].getValue());
                    options.setMaxDirLevel(idx, newMaxDirLevel);
                }
            });
        }

        return maxDirLevelFld[idx];
    }

    public void doAction() {
        // Set all values to their defaults.
        int[] defaultMaxDirLevels = new int[]{Options.defaultMaxDirCopyLevel, Options.defaultMaxDirFlattenLevel, Options.defaultMaxDirMergeLevel};
        for (int i = Options.MAX_DIR_COPY_IDX; i <= Options.MAX_DIR_MERGE_IDX; i++) {
            if ((Integer) maxDirLevelFld[i].getValue() != defaultMaxDirLevels[i]) {
                ;
            }
            {
                maxDirLevelFld[i].setValue(defaultMaxDirLevels[i]);
            }
        }

        for (int i = 0; i < Options.DIR_MERGE_FILTER + 1; i++) {
            srchFilterBoxes[i].setDefaults();
        }
    }

    class SrchFilterBox extends Box {

        private JTextField filterField = null;
        private JLabel filterLabel = null;
        private Boolean caseSensitive = null;
        private Boolean regEx = null;
        private Boolean exclusive = null;
        private FilenameFilter regExFilenameFilter = null;
        private JCheckBox caseSensitiveFileChk = null;
        private JCheckBox regExChk = null;
        private JCheckBox exclusiveChk = null;
        private Box checkBox = null;
        private String filterString = null;
        private int idx;
        private SrchFilter sf;
        private String[] filterFieldIncludeToolTipText = new String[Options.DIR_MERGE_FILTER + 1];
        private String[] filterFieldExcludeToolTipText = new String[Options.DIR_MERGE_FILTER + 1];
        private int regExFlags = 0;
        //private int filenameFlags = 0;
        public SrchFilterBox(final SrchFilter sf) {
            super(BoxLayout.PAGE_AXIS);
            idx = sf.ordinal();
            this.sf = sf;
            initialize();
        }

        private void initialize() {
            add(getFilterLabelBox());
            add(getFilterField());

            // Get the filter string.
            getFilterString();

            add(getCheckBox());
        }

        private Box getFilterLabelBox() {
            Box filterLabelBox = Box.createHorizontalBox();
            filterLabelBox.add(getFilterLabel());
            filterLabelBox.add(Box.createHorizontalGlue());
            return filterLabelBox;
        }

        private Box getCheckBox() {
            if (checkBox == null) {
                checkBox = new Box(BoxLayout.LINE_AXIS);
                checkBox.add(getCaseSensitiveChk());
                checkBox.add(getRegExChk());
                checkBox.add(getExclusiveChk());
                checkBox.add(Box.createHorizontalGlue());
            }
            return checkBox;
        }

        private JLabel getFilterLabel() {
            String[] toolTipText = new String[Options.DIR_MERGE_FILTER + 1];
            toolTipText[Options.FILE_COPY_FILTER] = "File copy name filter: ";
            toolTipText[Options.DIR_COPY_FILTER] = "Directory copy name filter: ";
            toolTipText[Options.DIR_FLATTEN_FILTER] = "Directory flatten name filter: ";
            toolTipText[Options.DIR_MERGE_FILTER] = "Directory merge name filter: ";
            if (filterLabel == null) {
                filterLabel = new JLabel(toolTipText[idx]);
            }
            return filterLabel;
        }

        private JTextField getFilterField() {
            if (filterField == null) {
                filterFieldIncludeToolTipText[Options.FILE_COPY_FILTER] = "File copy filter. File names must match this regular expression to be copied.";
                filterFieldIncludeToolTipText[Options.DIR_COPY_FILTER] = "Directory copy filter. Directory names must match this regular expression to be copied.";
                filterFieldIncludeToolTipText[Options.DIR_FLATTEN_FILTER] = "Directory flatten filter. Directory names match this regular expression to be flattened.";
                filterFieldIncludeToolTipText[Options.DIR_MERGE_FILTER] = "Directory merge filter. Directory names must match this regular expression to be merged.";
                filterFieldExcludeToolTipText[Options.FILE_COPY_FILTER] = "File copy filter. File names must not match this regular expression to be copied.";
                filterFieldExcludeToolTipText[Options.DIR_COPY_FILTER] = "Directory copy filter. Directory names must not match this regular expression to be copied.";
                filterFieldExcludeToolTipText[Options.DIR_FLATTEN_FILTER] = "Directory flatten filter. Directory names not match this regular expression to be flattened.";
                filterFieldExcludeToolTipText[Options.DIR_MERGE_FILTER] = "Directory merge filter. Directory names must not match this regular expression to be merged.";

                filterField = new JTextField();

                Dimension preferredSize = filterField.getPreferredSize();
                filterField.setMaximumSize(new Dimension(Short.MAX_VALUE, preferredSize.height));

                if (options.getExclusiveFilter(sf)) {
                    filterField.setToolTipText(filterFieldExcludeToolTipText[idx]);
                } else {
                    filterField.setToolTipText(filterFieldIncludeToolTipText[idx]);
                }


                filterField.setComponentPopupMenu(Utilities.createEditPopupMenu(filterField));

                // Set the text from the regex File name filter in options.
                FilenameFilter filenameFilter = options.getRegExFilenameFilter(sf);
                if (filenameFilter instanceof RegExFilenameFilter) {
                    filterField.setText(((RegExFilenameFilter) filenameFilter).pattern());
                }

                filterField.getDocument().addDocumentListener(new DocumentListener() {

                    public void changedUpdate(DocumentEvent de) {
                        updateFilenameFilter();
                    }

                    public void insertUpdate(DocumentEvent de) {
                        updateFilenameFilter();
                    }

                    public void removeUpdate(DocumentEvent de) {
                        updateFilenameFilter();
                    }

                    private void updateFilenameFilter() {
                        FilenameFilter filenameFilter = options.getRegExFilenameFilter(sf);
                        if (filenameFilter instanceof RegExFilenameFilter) {
                            RegExFilenameFilter regexFilenameFilter = (RegExFilenameFilter) filenameFilter;
                            regexFilenameFilter.compile(filterField.getText(), regexFilenameFilter.getRegexFlags());
                        } else {

                            int filenameFlags = 0;
                            if (exclusive) {
                                filenameFlags += RegExFilenameFilter.EXCLUSIVE;
                            }
                            if (idx == Options.FILE_COPY_FILTER) {
                                filenameFlags += RegExFilenameFilter.FILTERFILENAMES;
                            } else {
                                filenameFlags += RegExFilenameFilter.FILTERDIRNAMES;
                            }
                            filenameFilter = new RegExFilenameFilter(filterField.getText(), regExFlags, filenameFlags);
                            ((RegExFilenameFilter) filenameFilter).compile(filterField.getText(), regExFlags);
                            options.setRegExFilenameFilter(sf, filenameFilter);
                        }
                    }
                });
            }

            return filterField;
        }

        private String getFilterString() {
            if ((filterString == null) && (filterField != null)) {
                filterString = filterField.getText();
            }
            return filterString;
        }

        private FilenameFilter getRegExFilenameFilter() {
            if (regExFilenameFilter == null) {
                regExFilenameFilter = new RegExFilenameFilter(filterString, getRegExFlags());
                if (idx == Options.DIR_COPY_FILTER) {
                    // This is a filter for directory names, not file names so set the booleans accordingly.
                    ((RegExFilenameFilter) regExFilenameFilter).setFilenameFlags(RegExFilenameFilter.FILTERDIRNAMES);
                }
            }
            return regExFilenameFilter;
        }

        private int getRegExFlags() {
            return regExFlags;
        }

        private JCheckBox getCaseSensitiveChk() {
            if (caseSensitiveFileChk == null) {
                String[] toolTipText = new String[Options.DIR_MERGE_FILTER + 1];
                toolTipText[Options.FILE_COPY_FILTER] = "Make file copy filter case sensitive.";
                toolTipText[Options.DIR_COPY_FILTER] = "Make directory copy filter case sensitive.";
                toolTipText[Options.DIR_FLATTEN_FILTER] = "Make directory flatten filter case sensitive.";
                toolTipText[Options.DIR_MERGE_FILTER] = "Make directory merge filter case sensitive.";

                char[] mnemonic = new char[Options.DIR_MERGE_FILTER + 1];
                mnemonic[Options.FILE_COPY_FILTER] = java.awt.event.KeyEvent.VK_C;
                mnemonic[Options.DIR_COPY_FILTER] = java.awt.event.KeyEvent.VK_N;
                mnemonic[Options.DIR_FLATTEN_FILTER] = java.awt.event.KeyEvent.VK_T;
                mnemonic[Options.DIR_MERGE_FILTER] = java.awt.event.KeyEvent.VK_V;

                caseSensitiveFileChk = new JCheckBox("Case Sensitive");
                caseSensitiveFileChk.setMnemonic(mnemonic[idx]);
                caseSensitiveFileChk.setToolTipText(toolTipText[idx]);
                caseSensitive = options.getCaseSensitiveFilter(sf);
                caseSensitiveFileChk.setSelected(caseSensitive);
                if (!caseSensitive) {
                    regExFlags += Pattern.CASE_INSENSITIVE;
                }
                caseSensitiveFileChk.addActionListener(new java.awt.event.ActionListener() {

                    public void actionPerformed(java.awt.event.ActionEvent e) {
                        caseSensitive = !caseSensitive;
                        if (!caseSensitive) {
                            regExFlags += Pattern.CASE_INSENSITIVE;
                        } else {
                            regExFlags -= Pattern.CASE_INSENSITIVE;
                        }
                        options.setCaseSensitiveFilter(sf, caseSensitive);
                        FilenameFilter filenameFilter = options.getRegExFilenameFilter(sf);
                        if (filenameFilter instanceof RegExFilenameFilter) {
                            ((RegExFilenameFilter) filenameFilter).setRegexFlags(regExFlags);
                        }
                    }
                });
            }
            return caseSensitiveFileChk;
        }

        private JCheckBox getRegExChk() {
            if (regExChk == null) {
                String[] toolTipText = new String[Options.DIR_MERGE_FILTER + 1];
                toolTipText[Options.FILE_COPY_FILTER] = "Treat file copy filter as a regular expression.";
                toolTipText[Options.DIR_COPY_FILTER] = "Treat directory copy filter as a regular expression.";
                toolTipText[Options.DIR_FLATTEN_FILTER] = "Treat directory flatten filter as a regular expression.";
                toolTipText[Options.DIR_MERGE_FILTER] = "Treat directory merge filter as a regular expression.";

                char[] mnemonic = new char[Options.DIR_MERGE_FILTER + 1];
                mnemonic[Options.FILE_COPY_FILTER] = java.awt.event.KeyEvent.VK_R;
                mnemonic[Options.DIR_COPY_FILTER] = java.awt.event.KeyEvent.VK_G;
                mnemonic[Options.DIR_FLATTEN_FILTER] = java.awt.event.KeyEvent.VK_I;
                mnemonic[Options.DIR_MERGE_FILTER] = java.awt.event.KeyEvent.VK_O;

                regExChk = new JCheckBox("Regular Expression");
                regExChk.setMnemonic(mnemonic[idx]);
                regExChk.setToolTipText(toolTipText[idx]);
                regEx = options.getRegExFilter(sf);
                regExChk.setSelected(regEx);
                if (!regEx) {
                    regExFlags += Pattern.LITERAL;
                }
                regExChk.addActionListener(new java.awt.event.ActionListener() {

                    public void actionPerformed(java.awt.event.ActionEvent e) {
                        regEx = !regEx;
                        if (!regEx) {
                            regExFlags += Pattern.LITERAL;
                        } else {
                            regExFlags -= Pattern.LITERAL;
                        }
                        options.setRegExFileFilter(sf, regEx);
                        FilenameFilter filenameFilter = options.getRegExFilenameFilter(sf);
                        if (filenameFilter instanceof RegExFilenameFilter) {
                            ((RegExFilenameFilter) filenameFilter).setRegexFlags(regExFlags);
                        }
                    }
                });

            }
            return regExChk;
        }

        private JCheckBox getExclusiveChk() {
            if (exclusiveChk == null) {
                String[] toolTipText = new String[Options.DIR_MERGE_FILTER + 1];
                toolTipText[Options.FILE_COPY_FILTER] = "Make file copy filter exclusive.";
                toolTipText[Options.DIR_COPY_FILTER] = "Make directory copy filter exclusive.";
                toolTipText[Options.DIR_FLATTEN_FILTER] = "Make directory flatten filter exclusive.";
                toolTipText[Options.DIR_MERGE_FILTER] = "Make directory merge filter exclusive.";

                char[] mnemonic = new char[Options.DIR_MERGE_FILTER + 1];
                mnemonic[Options.FILE_COPY_FILTER] = java.awt.event.KeyEvent.VK_J;
                mnemonic[Options.DIR_COPY_FILTER] = java.awt.event.KeyEvent.VK_K;
                mnemonic[Options.DIR_FLATTEN_FILTER] = java.awt.event.KeyEvent.VK_X;
                mnemonic[Options.DIR_MERGE_FILTER] = java.awt.event.KeyEvent.VK_L;

                exclusiveChk = new JCheckBox("Exclusive");
                exclusiveChk.setMnemonic(mnemonic[idx]);
                exclusiveChk.setToolTipText(toolTipText[idx]);
                exclusive = options.getExclusiveFilter(sf);
                exclusiveChk.setSelected(exclusive);

                exclusiveChk.addActionListener(new java.awt.event.ActionListener() {

                    public void actionPerformed(java.awt.event.ActionEvent e) {
                        exclusive = !exclusive;
                        options.setExclusiveFilter(sf, exclusive);
                        FilenameFilter filenameFilter = options.getRegExFilenameFilter(sf);
                        if (filenameFilter instanceof RegExFilenameFilter) {
                            if (exclusive) {
                                ((RegExFilenameFilter) filenameFilter).turnOnFilenameFlag(RegExFilenameFilter.EXCLUSIVE);
                            } else {
                                ((RegExFilenameFilter) filenameFilter).turnOffFilenameFlag(RegExFilenameFilter.EXCLUSIVE);
                            }
                        }
                        if (exclusive) {
                            filterField.setToolTipText(filterFieldExcludeToolTipText[idx]);
                        } else {
                            filterField.setToolTipText(filterFieldIncludeToolTipText[idx]);
                        }
                    }
                });
            }
            return exclusiveChk;
        }

        private FilenameFilter setRegExFilenameFilter(FilenameFilter filenameFilter) {
            if (filenameFilter != null) {
                filterString = ((RegExFilenameFilter) filenameFilter).pattern();
                filterField.setText(filterString);
            }
            return filenameFilter;
        }

        private boolean setCaseSensitiveFilter(boolean filterSet) {
            caseSensitiveFileChk.setSelected(filterSet);
            return filterSet;
        }

        private boolean setRegExFilter(boolean filterSet) {
            regExChk.setSelected(filterSet);
            return filterSet;
        }

        private boolean setExclusiveFilter(boolean filterSet) {
            exclusiveChk.setSelected(filterSet);
            return filterSet;
        }

        private void setRegExFlags(final boolean caseSensitiveSet, final boolean regExSet) {
            regExFlags = 0;
            ((RegExFilenameFilter) regExFilenameFilter).compile(filterString, regExFlags);
        }

        public void setDefaults() {
            if (!filterField.getText().equals(Options.defaultRegExFilenameFilterStr)) {
                filterField.setText(Options.defaultRegExFilenameFilterStr);
            }
            if (caseSensitiveFileChk.isSelected() != Options.defaultCaseSensitiveFilter) {
                caseSensitiveFileChk.doClick();
            }
            if (regExChk.isSelected() != Options.defaultRegExFilter) {
                regExChk.doClick();
            }
            if (exclusiveChk.isSelected() != Options.defaultExclusiveFilter) {
                exclusiveChk.doClick();
            }
        }
    }
}
